#include "gut/Shader.h"

using namespace Gut;

bool ShaderCode::checkCompileErrors(unsigned int shader, std::string type)

{
	GLint success;
	GLchar infoLog[1024];
	if (type != "PROGRAM")
	{
		glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
		if (!success)
		{
			glGetShaderInfoLog(shader, 1024, NULL, infoLog);
			GUT_LOGE("ERROR::SHADER_COMPILATION_ERROR of type: %s \n %s", type.c_str(), infoLog);
		}
	}
	else
	{
		glGetProgramiv(shader, GL_LINK_STATUS, &success);
		if (!success)
		{
			glGetProgramInfoLog(shader, 1024, NULL, infoLog);
			GUT_LOGE("ERROR::PROGRAM_LINKING_ERROR of type: %s \n %s", type.c_str(), infoLog);
		}
	}
	return success;
}
std::string ShaderCode::GetShaderType(int type)
{
	switch (type)
	{
	case GL_VERTEX_SHADER:
		return "Vertex";
	case GL_FRAGMENT_SHADER:
		return "Fragment";
	default:
		break;
	}
	return "Unknown";
}

bool ShaderFile::CreateFromFile(std::string path, int type)
{
	bool gr = true;

	std::ifstream ifs(path, std::ios::in);

	if (!ifs.is_open())
	{
		GUT_LOGI("cannot open file from %s ", path.c_str());
		ifs.close();
		return false;
	}

	GUT_LOGI("Loading the file %s", path.c_str());

	std::string text = std::string((std::istreambuf_iterator<char>(ifs)),
								   (std::istreambuf_iterator<char>()));

	gr = CreateFrom(text.c_str(), type);
	ifs.close();
	return gr;
}

bool Shader::CreateFromCode(const char *vcode, const char *fcode)
{
	ShaderFile vFile;
	ShaderFile fFile;

	GUT_FAILED(vFile.CreateFrom(vcode, GL_VERTEX_SHADER));
	GUT_FAILED(fFile.CreateFrom(fcode, GL_FRAGMENT_SHADER));

	this->ID = glCreateProgram();

	glAttachShader(this->ID, vFile.id);
	glAttachShader(this->ID, fFile.id);

	glLinkProgram(ID);

	return true;
}

bool Shader::CreateFromFile(ShaderFile vFile, ShaderFile fFile)
{
	this->ID = glCreateProgram();
	glAttachShader(this->ID, vFile.id);
	glAttachShader(this->ID, fFile.id);

	glLinkProgram(ID);
	return true;
}

bool Shader::CreateFromFilePath(std::string vPath, std::string fPath)
{
	bool gr;

	ShaderFile vFile;
	ShaderFile fFile;

	GUT_FAILED(vFile.CreateFromFile(vPath, GL_VERTEX_SHADER));
	GUT_FAILED(fFile.CreateFromFile(fPath, GL_FRAGMENT_SHADER));

	return CreateFromFile(vFile, fFile);
}

bool ShaderCode::CreateFrom(const char *code, int type)
{
	this->id = glCreateShader(type);
	glShaderSource(id, 1, &code, NULL);
	glCompileShader(id);
	if (!checkCompileErrors(id, GetShaderType(type)))
	{
		return false;
	}
	return true;
}

void ShaderCode::Release()
{
	glDeleteShader(this->id);
}

void Shader::Use() const
{
	glUseProgram(ID);
}

void Shader::Create()
{
	ID = glCreateProgram();
}

void Shader::Release()
{

	glDeleteProgram(this->ID);
}

void Shader::Link()
{
	glLinkProgram(ID);
}

void Shader::UnUse() const
{
	glUseProgram(0);
}
